#pragma once

#include "..\\ExternalTools\\ExternalTools.h"

#include "..\\Loirey\\loirey_GLOBAL.h"

#include "..\\Loirey\\loirey_BasicStructure.h"
#include "..\\Loirey\\loirey_Configuration.h"
#include "..\\Loirey\\loirey_MyMath.h"

#include "..\\Classification.Base\\cl_base_BinaryClassification.h"

#include "..\\headers.ipfe.h"

#include "det_SlidingBox.h"

using namespace loirey;

//////////

class CDetectionWithScore
{
public:
	CRectBox<double> Detection;
	double Score;
	bool fIsMatched;

public:
	bool operator< (const CDetectionWithScore& AnotherInstance) const
	{
		return Score > AnotherInstance.Score;
	}
public:
	static int CheckDetections(
		CSimpleTypeArray<CDetectionWithScore>& DetectionList, 
		CSimpleTypeArray<CDetectionWithScore>& GroundTruthList,
		double AO_Threshold = 0.50,
		bool fAllowMultipleDetections = false)
	{
		int i, j, j_max;
		double AO, AO_max;

		int nd = (int)DetectionList.Size;
		int ng = (int)GroundTruthList.Size;
		CDetectionWithScore* pd = DetectionList.pElement;
		CDetectionWithScore* pg = GroundTruthList.pElement;

		int ret = ng;

		sort(DetectionList.Begin(), DetectionList.End());
		for ( j = 0; j < ng; ++j ) pg[j].fIsMatched = false;

		for ( i = 0; i < nd; ++i )
		{
			pd[i].fIsMatched = false;

			for ( j_max = -1, j = 0; j < ng; ++j )
			{
				AO = CRectBox<double>::CalcAO_Percent(pd[i].Detection, pg[j].Detection);
				if (j_max>=0 && AO<AO_max) continue;
				AO_max = AO;
				j_max = j;
			}
			if (j_max<0 || AO_max<AO_Threshold) continue;
			if (pg[j_max].fIsMatched && !fAllowMultipleDetections) continue;

			pd[i].fIsMatched = true;
			if (!pg[j_max].fIsMatched) --ret;
			pg[j_max].fIsMatched = true;
		}

		return ret;
	}
};

//////////

class CCandidateDetection
{
public:
	CImagePyramidPosition ipp;
	double Confidence;
	int Tag;

public:
	static bool CompareBy_Bigger_Confidence(const CCandidateDetection& a, const CCandidateDetection& b);
	static bool CompareBy_hwyx(const CCandidateDetection& a, const CCandidateDetection& b);
	static bool CompareBy_Tag(const CCandidateDetection& a, const CCandidateDetection& b);

	static bool InputListFromStream(istream& inStream, CSimpleTypeArray<CCandidateDetection>& dstList);
	static bool InputListFromFile(const char* strFN_CanDetList, CSimpleTypeArray<CCandidateDetection>& dstList);
	static bool InputListFromFile(const string& strFN_CanDetList, CSimpleTypeArray<CCandidateDetection>& dstList);
	static void OutputListToStream(ostream& outStream, const CSimpleTypeArray<CCandidateDetection>& srcList);
	static void OutputListToFile(const char* strFN_CanDetList, const CSimpleTypeArray<CCandidateDetection>& srcList);
	static void OutputListToFile(const string& strFN_CanDetList, const CSimpleTypeArray<CCandidateDetection>& srcList);
public:
	CCandidateDetection();
	void Build(CImagePyramidPositionFeatureExtractor* pIPPFE, CBinaryClassifier* pClassifier, int Tag = 0);
	void Build(const CImagePyramidPosition& ipp, CImagePyramidPositionFeatureExtractor* pIPPFE, CBinaryClassifier* pClassifier, int Tag = 0);

	bool InputFromStream(istream& inStream);
	void OutputToStream(ostream& outStream);
};

class CNonMaximumSupperessor
{
public:
	virtual void DoNMS(
		CSimpleTypeArray<CCandidateDetection>& dstDetectionList,
		const CSimpleTypeArray<CCandidateDetection>& srcDetectionList
		) = 0;
};

class CSlidingWindowDetector
{
public:
	CImagePyramidPositionFeatureExtractor* pIPPFE;
	CBinaryClassifier* pClassifier;
	CNonMaximumSupperessor* pNMSor;
public:
	CSimpleTypeArray<CCandidateDetection> CandidateDetectionList;
	CSimpleTypeArray<CCandidateDetection> ReducedDetectionList;
public:
	static int CheckDetections(
		CSimpleTypeArray<CCandidateDetection>& DetectionList,
		CSimpleTypeArray<CRectBox<int>>& GtBoxList,
		double BaseDetectionBoxW, double BaseDetectionBoxH,
		double AO_Threshold, bool fAllowMultiDetection
		);
public:
	void myInit(
		CImagePyramidPositionFeatureExtractor* pIPPFE,
		CBinaryClassifier* pClassifier,
		CNonMaximumSupperessor* pNMSor
		);
	void SetImage(const CImg<int>& imgSourceRGB);
	void BuildCandidateDetectionList(const CImageWindowContext::CSlidingWindowList& swl);
	void DoNMS();
	double Classify(const CImagePyramidPosition& ipp);
};

class CMeanShiftNMS : public CNonMaximumSupperessor
{
public:
	static const int TFMODE_HARD = 0;
	static const int TFMODE_SOFT = 1;
	static const int TFMODE_SIGMOID = 2;
	static const int TFMODE_LOG_HARD = 3;

public:
	class CKernelConfig
	{
	public:
		double dx, dy, dw, dh;
		int tfMode;
		double center, scale;
	};
	class CParameters
	{
	public:
		CKernelConfig KernelConfig;
		double ConfidenceThreshold_Before;
		double ConfidenceThreshold_After;

	public:
		void SetConfidenceThreshold(double ConfidenceThreshold);
	};
	class CNode
	{
	public:
		double X,Y,lnW,lnH;

	public:
		static double GetDistance2(const CNode& a, const CNode& b);
	};
	class CKernelNode
	{
	public:
		CNode node;
		double inv_dx;
		double inv_dy;
		double inv_dw;
		double inv_dh;
		double log_transformed_weight;
		double log_coefficient;

	public:
		static double Calc_log_TransformFunction(double Confidence, const CKernelConfig& KernelConfig);
	public:
		void myInit(double Confidence, const CKernelConfig& KernelConfig);
		void myInit(const CNode& Node, double Confidence, const CKernelConfig& KernelConfig);
	};

protected:
	CParameters _Parameters;
	CImagePyramidPositionFeatureExtractor* __pIPPFE;
	CBinaryClassifier* __pClassifier;
public:
	CSimpleTypeArray<CKernelNode> KernelNodes;

public:
	CMeanShiftNMS();
	void ClearKernelNodes();
	void myInit(
		const CParameters& Parameters,
		CImagePyramidPositionFeatureExtractor* pIPPFE,
		CBinaryClassifier* pClassifier
		);
public:
	void BuildNode(CNode& dstNode, const CImagePyramidPosition& src_ipp);
	void BuildIPP(CImagePyramidPosition& dst_ipp, const CNode& srcNode);
	void BuildKernelNode(CKernelNode& dstKernelNode, const CCandidateDetection& srcDetection);
public:
	void Insert(const CCandidateDetection& srcDetection);
	void Insert(const CSimpleTypeArray<CCandidateDetection>& srcDetectionList);
public:
	double Promote(CNode& dstNode, const CNode& srcNode);
	double Promote(CNode& Node);
public:
	void DoNMS(
		CSimpleTypeArray<CCandidateDetection>& dstDetectionList,
		const CSimpleTypeArray<CCandidateDetection>& srcDetectionList
		);
};

